#pragma once

#include <cstring>
#include <string>

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>

#include "Log.hpp"
#include "Erro.hpp"

struct Sock
{
    static const int backlog = 32;

    /// @brief 创建一个新的套接字
    /// @return 新创建的套接字描述符
    static int Socket()
    {
        int sock = socket(AF_INET, SOCK_STREAM, 0);
        if (sock == -1)
        {
            logMessage(FATAL, "Create soket erro");
            exit(SOCKET_ERROR);
        }
        logMessage(NORMAL, "Create a new socket: %d", sock);

        int opt = 1;
        setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
        return sock;
    }

    /// @brief 将套接字绑定到指定的端口
    /// @param sock 要绑定的套接字
    /// @param port 要绑定的端口号
    static void Bind(int sock, int port)
    {
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(port);
        local.sin_addr.s_addr = INADDR_ANY;

        if (bind(sock, (const struct sockaddr *)&local, sizeof(local)) < 0)
        {
            logMessage(FATAL, "bind socket erro");
            exit(BIND_ERROR);
        }
        logMessage(NORMAL, "bind socket succes");
    }

    /// @brief 监听指定的套接字，等待连接请求
    /// @param sock 需要监听的套接字
    static void Listen(int sock)
    {
        if (listen(sock, backlog) < 0)
        {
            logMessage(FATAL, "bind socket erro");
            exit(LISTEN_ERROR);
        }
        logMessage(NORMAL, "bind socket succes");
    }

    /// @brief 在监听套接字上接受新的客户端连接。
    /// @param listensock 监听套接字的文件描述符。
    /// @param clientip 指向 std::string 的指针，用于存储客户端的 IP 地址。
    /// @param clientport 指向 uint16_t 的指针，用于存储客户端的端口号。
    /// @return 已接受客户端的套接字文件描述符，如果出现错误则返回 -1。
    static int Accept(int listensock, std::string *clientip, uint16_t *clientport)
    {
        struct sockaddr_in client;
        memset(&client, 0, sizeof(client));
        socklen_t clientlen = sizeof(client);
        int sockfd = accept(listensock, (struct sockaddr *)&client, &clientlen);
        if (sockfd < 0)
        {
            logMessage(ERROR, "accept error");
        }
        else
        {
            logMessage(NORMAL, "accept succes, sock: %d, client ip: %s", sockfd, inet_ntoa(client.sin_addr));
            *clientip = inet_ntoa(client.sin_addr);
            *clientport = htons(client.sin_port);
        }
        return sockfd;
    }
};